<template>
  <div ref="statsRef"></div>
  <div ref="containerRef" class="container">
  </div>
</template>
<script lang="ts" setup>
import {onMounted, ref, watch} from "vue";
import {
  AmbientLight,
  AxesHelper, BoxGeometry, BufferGeometry,
  Color, CylinderGeometry, Fog,
  Mesh,
  MeshBasicMaterial, MeshLambertMaterial, OctahedronGeometry,
  PerspectiveCamera,
  PlaneGeometry,
  Scene, SphereGeometry, SpotLight, TetrahedronGeometry, TorusGeometry,
  WebGLRenderer
} from "three";
import Stats from "stats.js"
import * as dat from "dat.gui"
import {createMultiMaterialObject} from "three/examples/jsm/utils/SceneUtils";

const containerRef = ref<HTMLDivElement>()
const statsRef = ref<HTMLDivElement>()

const stats = new Stats()
stats.showPanel(0)

//创建场景
const scene = new Scene();
//创建摄像机
const camera = new PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000)
//设置摄像机位置
camera.position.set(-30,40,30)
//设置摄像机朝向
camera.lookAt(scene.position)

//重置webGL的颜色
const renderer =  new WebGLRenderer();
renderer.setClearColor(new Color(0xeeeeee))
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled = true

const spotLight = new SpotLight(0xffffff)
spotLight.castShadow = true
spotLight.position.set(-40,60,-10)
scene.add(spotLight)

//添加坐标系
const axes = new AxesHelper(20)
scene.add(axes)

//绘制板子，设置板子的宽度为60，设置板子的高度为20
const planeGeometry = new PlaneGeometry(100,50);
const meshBasicMaterial = new MeshLambertMaterial({color:0xcccccc});//设置材质颜色
const plane = new Mesh(planeGeometry,meshBasicMaterial)
plane.receiveShadow = true //设置可以接收阴影
plane.rotation.x = -0.5 * Math.PI;
//plane.position.x = 15
//plane.position.y = 0
//plane.position.z = 0

scene.add(plane)


//绘制立方体，设置板子的长宽高分别是4,4,4
const cubeGeometry = new BoxGeometry(4,4,4)
const cubeMaterial = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube = new Mesh(cubeGeometry,cubeMaterial)
cube.castShadow = true
cube.position.set(2,2,2)
scene.add(cube)


//绘制立方体，设置板子的长宽高分别是4,4,4
const cubeGeometry1 = new BoxGeometry(4,4,4)
const cubeMaterial1 = new MeshLambertMaterial({color:0xff0000,wireframe:false})
const cube1 = new Mesh(cubeGeometry1,cubeMaterial1)
cube1.castShadow = true
cube1.position.set(-10,2,2)
scene.add(cube1)



//绘制球体，设置球体的半径为4
const sphereGeometry = new SphereGeometry(4)
const sphereMaterial = new MeshLambertMaterial({
  color: 0x7777ff,
  wireframe:false
})

const sphere = new Mesh(sphereGeometry,sphereMaterial)
sphere.castShadow = true
sphere.position.x = 15
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)

const geoms:BufferGeometry[] = []
geoms.push(new CylinderGeometry(1,4,8))
geoms.push(new BoxGeometry(2,2,2))
geoms.push(new OctahedronGeometry(3))
geoms.push(new TetrahedronGeometry(3))
geoms.push(new TorusGeometry(3,1,10,10))
//材质
const materials = [
  new MeshLambertMaterial({
    color:Math.random() * 0xffffff,
    flatShading:true
  }),
  new MeshBasicMaterial({
    color: 0x000000,
    wireframe:true
  })
]
geoms.forEach((g,i) =>{
  const mesh = createMultiMaterialObject(g,materials)
  mesh.name = "geometry",
      mesh.castShadow = true
  mesh.position.x = -24 + i * 10,
      mesh.position.y = 4,
      scene.add(mesh)
})



const controlRef = ref({
  rotationSpeed:0.02,
  bouncingSpeed:0.03,
  numberOfObjects:0,
  addCube:function (){
    //绘制立方体，设置板子的长宽高分别是4,4,4
    const cubeGeometry = new BoxGeometry(4,4,4)
    const cubeMaterial = new MeshLambertMaterial({color:0xff0000,wireframe:false})
    const cube = new Mesh(cubeGeometry,cubeMaterial)
    cube.name = "cube-"+scene.children.length
    cube.castShadow = true
    cube.position.x = -30 + Math.round((Math.random() * 60))
    cube.position.y =  Math.round((Math.random() * 5))
    cube.position.z = -20 + Math.round((Math.random() * 40))
    scene.add(cube)
    this.numberOfObjects = scene.children.length
  },
  removeCube:function (){
    const allChildren = scene.children;
    const lastObject = allChildren[allChildren.length - 1];
    if(lastObject instanceof Mesh && lastObject.name.startsWith('cube')){
      scene.remove(lastObject);
    }
    this.numberOfObjects = scene.children.length
  },
  addFog:function (){
    scene.fog = new Fog(0xffffff,0.015,100)
    this.numberOfObjects = scene.children.length
  },
  removeFog:function (){
    scene.fog = null
  },
  toggleMaterial:function (){
    if(!scene.overrideMaterial){
      scene.overrideMaterial = new MeshLambertMaterial({
        color:0xffffff,
      })
    }else{
      scene.overrideMaterial =null
    }
  },
  scaleX:1,
  scaleY:1,
  scaleZ:1,
  positionX:1,
  positionY:1,
  positionZ:1,
  translateZ:1,
  translateY:1,
  translateX:1,
  obj:{
    x:0,
    y:0,
    z:0,
  },
  translate:function (){
    this.obj.x = this.translateX
    this.obj.y = this.translateY
    this.obj.z = this.translateZ
  },
  ambientColor:"#0c0c0c"
})

const ambientLight = new AmbientLight(controlRef.value.ambientColor)
scene.add(ambientLight)

watch(()=>controlRef.value.ambientColor,(n)=>{
  ambientLight.color = new Color(n)
})

if(document.querySelectorAll(".dg.ac>.dg.main.a").length === 0){
  const gui = new dat.GUI()
  gui.addColor(controlRef.value,"ambientColor")
  gui.add(controlRef.value,"addCube")
  gui.add(controlRef.value,"removeCube")
  gui.add(controlRef.value,"addFog")
  gui.add(controlRef.value,"removeFog")
  gui.add(controlRef.value,"toggleMaterial")
  gui.add(controlRef.value,"numberOfObjects").listen()
  gui.add(controlRef.value,"rotationSpeed",0,0.5)
  gui.add(controlRef.value,"bouncingSpeed",0,0.5)
  /*设置大小*/
  const scaleFolder = gui.addFolder("scale")
  scaleFolder.add(controlRef.value,"scaleX",0,5);
  scaleFolder.add(controlRef.value,"scaleY",0,5);
  scaleFolder.add(controlRef.value,"scaleZ",0,5);
  /*设置位置*/
  const positionFolder = gui.addFolder("position")
  positionFolder.add(controlRef.value,"positionX",-5,5);
  positionFolder.add(controlRef.value,"positionY",-5,5);
  positionFolder.add(controlRef.value,"positionZ",-5,5);

  /*设置斜角位置*/
  const translateFolder = gui.addFolder("translate")
  translateFolder.add(controlRef.value,"translateX",-5,5);
  translateFolder.add(controlRef.value,"translateY",-5,5);
  translateFolder.add(controlRef.value,"translateZ",-5,5);
  translateFolder.add(controlRef.value,"translate");

}

watch(()=>controlRef.value.scaleX,(n)=>{
  cube.scale.setX(n)
})
watch(()=>controlRef.value.scaleY,(n)=>{
  cube.scale.setY(n)
})
watch(()=>controlRef.value.scaleZ,(n)=>{
  cube.scale.setZ(n)
})
watch(()=>controlRef.value.positionX,(n)=>{
  cube.position.setX(n * 10)
})
watch(()=>controlRef.value.positionY,(n)=>{
  cube.position.setY(n * 10)
})
watch(()=>controlRef.value.positionZ,(n)=>{
  cube.position.setZ(n * 10)
})

watch(()=>controlRef.value.obj,(n)=>{
  cube.translateX(n.x)
  cube.translateY(n.y)
  cube.translateZ(n.z)
},{deep:true})


//控制物体运动
let step = 0;

function renderScene() {

  stats.update()

  //遍历场景当中的所有内容
  scene.traverse((e) =>{
    /*if ( e instanceof Mesh && e != plane){
      e.rotation.x += controlRef.value.rotationSpeed;
      e.rotation.y += controlRef.value.rotationSpeed;
      e.rotation.z += controlRef.value.rotationSpeed;
    }*/

    if ( e.name.startsWith('cube')){
      e.rotation.x += controlRef.value.rotationSpeed;
      e.rotation.y += controlRef.value.rotationSpeed;
      e.rotation.z += controlRef.value.rotationSpeed;
    }

    if ( e.name.startsWith('geometry')){
      e.rotation.x += controlRef.value.rotationSpeed;
      e.rotation.y += controlRef.value.rotationSpeed;
      e.rotation.z += controlRef.value.rotationSpeed;
    }

  })

  step += 0.04;

  cube.rotation.x += controlRef.value.rotationSpeed;
  cube.rotation.y += controlRef.value.rotationSpeed;
  cube.rotation.z += controlRef.value.rotationSpeed;

  cube1.rotation.x += -controlRef.value.rotationSpeed;
  cube1.rotation.y += -controlRef.value.rotationSpeed;
  cube1.rotation.z += -controlRef.value.rotationSpeed;

  step += controlRef.value.bouncingSpeed;

  cube1.scale.set((2 + 1 * Math.cos(step)), (2 + 1 * Math.cos(step)), (2 + 1 * Math.cos(step)));
  //控制物体
  sphere.position.x = 20 + 10 * Math.cos(step); //cos为数据当中的函数 余弦函数
  sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));  //abs为绝对值  sin为正弦函数

  requestAnimationFrame(renderScene)
  renderer.render(scene,camera)
}
renderScene()

/*
监听在窗口变化的时候重新设置大小
* */
window.addEventListener('resize',()=>{
  camera.aspect = window.innerWidth / window.innerHeight;
  //更新相机投影矩阵
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth,window.innerHeight)
})


onMounted(()=>{

  //创建场景
  const scene = new Scene();
  stats.dom.style.top = "50px"
  statsRef.value?.append(stats.dom)

  //设置摄像头朝向
  containerRef.value?.appendChild(renderer.domElement)
  renderer.render(scene,camera)

})

</script>
<style scoped>

</style>
